home *** CD-ROM | disk | FTP | other *** search
- // http://lists.w3.org/Archives/Public/www-archive/2009Sep/att-0051/draft-hodges-strict-transport-sec-05.plain.html
-
- const STS = {
-
- enabled: true,
-
- get db() {
- delete this.db;
- return this.initPersistentDB();
- },
-
- initPersistentDB: function() {
- return this.db = new STSDB(STSPersistence);
- },
-
- processRequest: function(chan) {
- if (this.enabled) {
- var uri = chan.URI;
- if (uri.schemeIs("https")) {
- try {
- this.db.processHeader(uri.asciiHost, chan.getResponseHeader("Strict-Transport-Security"));
- } catch (e) {
- try {
- this.db.processHeader(uri.asciiHost, chan.getResponseHeader("X-Strict-Transport-Security"));
- } catch (e) {}
- }
- }
- }
- },
-
- isSTSURI: function(uri) {
- return this.enabled && this.db.matches(uri.asciiHost);
- },
-
- enterPrivateBrowsing: function() {
- try {
- this.db.save();
- } catch(e) {}
-
- this.db = new STSDB(this.db);
- },
-
- exitPrivateBrowsing: function() {
- this.initPersistentDB();
- },
-
- eraseDB: function() {
- this.db.reset();
- STSPersistence.save(this.db);
- },
-
- patchErrorPage: function(docShell, errorURI) {
- // see #errors-in-secure-transport-establishment
- if (!this.enabled) return;
-
- if (!(/^about:certerror?/.test(errorURI.spec) &&
- this.isSTSURI(docShell.currentURI))
- ) return;
-
- Thread.delay(function() {
- docShell.document.getElementById("expertContent").style.display = "none";
- }, 100);
- },
-
- dispose: function() {
- this.db.save();
- }
- };
-
- function STSDB(source) {
- this._entries = {};
- if (source && source._entries) { // clone
- var entries = source._entries;
- for (var p in entries) {
- this._entries[p] = entries[p];
- }
- } else {
- if (source && source.load) {
- this._persistence = source;
- this.load();
- }
- }
- }
-
- STSDB.prototype = {
- _persistence: null,
- _dirty: false,
- _saveTimer: null,
-
- processHeader: function(host, header) {
- if (DNS.isIP(host)) return;
-
- var m = header.match(/^\s*max-age\s*=\s*(\d+)\s*(;\s*includeSubDomains)?/i);
- if (!m) return;
- var maxAge = parseInt(m[1]);
- var includeSubDomains = !!m[2];
- var expiration = Math.round(Date.now() / 1000) + maxAge;
- if (host in this._entries) {
- var e = this._entries[host];
- if (e.expiration == expiration && e.includeSubDomains == includeSubDomains)
- return;
-
- e.expiration = expiration;
- e.includeSubDomains = includeSubDomains;
- } else {
- this.add(new STSEntry(host, expiration, includeSubDomains));
- }
- this.saveDeferred();
- },
-
- add: function(entry) {
- this._entries[entry.host] = entry;
- },
-
- matches: function(host, asSuperDomain) {
- if (host in this._entries) {
- var e = this._entries[host];
-
- if (e.expiration >= Date.now() / 1000) {
- if ((!asSuperDomain || e.includeSubDomains))
- return true;
- } else {
- delete this._entries[host];
- }
- }
-
- var dotPos = host.indexOf(".");
- var lastDotPos = host.lastIndexOf(".");
-
- if (dotPos == lastDotPos)
- return false;
-
- return this.matches(host.substring(dotPos + 1), true);
- },
-
- serialize: function() {
- var lines = [], ee = this._entries;
- var e;
- for (var h in ee) {
- e = ee[h];
- lines.push([e.host, e.expiration, e.includeSubDomains ? "*" : ""].join(";"));
- }
- return lines.join("\n");
- },
- restore: function(s) {
- s.split(/\s+/).forEach(function(line) {
- if (line) {
- var args = line.split(";");
- if (args.length > 1)
- this.add(new STSEntry(args[0], parseInt(args[1]), !!args[2]));
- }
- }, this);
- },
-
- load: function() {
- if (this._persistence) {
- this._persistence.load(this);
- this.purgeExpired();
- }
- },
-
- save: function() {
- if (this._dirty && this._persistence) {
- this.purgeExpired();
- this._persistence.save(this);
- this._dirty = false;
- }
- },
-
- saveDeferred: function() {
- if (this._dirty || !this._persistence) return;
- this._dirty = true;
- if (!this._timer) this._timer = CC["@mozilla.org/timer;1"].createInstance(CI.nsITimer);
- this._timer.initWithCallback(this, 10000, CI.nsITimer.TYPE_ONE_SHOT);
- },
- notify: function(timer) {
- this.save();
- },
-
- purgeExpired: function() {
- var now = Math.round(Date.now() / 1000);
- for (var h in this._entries) {
- if (this._entries[h].expiration < now) delete this._entries[h];
- }
- },
-
- reset: function() {
- this._entries = {};
- this._dirty = false;
- }
- };
-
- function STSEntry(host, expiration, includeSubDomains) {
- this.host = host;
- this.expiration = expiration;
- if (includeSubDomains) this.includeSubDomains = includeSubDomains;
- }
-
- STSEntry.prototype = {
- includeSubDomains: false
- };
-
-
- const STSPersistence = {
- get _file() {
- delete this._file;
- var f = CC["@mozilla.org/file/directory_service;1"].getService(
- CI.nsIProperties).get("ProfD", CI.nsIFile);
- f.append("NoScriptSTS.db");
- return this._file = f;
- },
- load: function(db) {
- var f = this._file;
- try {
- if (f.exists()) db.restore(IO.readFile(f));
- } catch (e) {
- dump("STS: Error loading db from " + f.path + "!" + e + "\n");
- return false;
- }
- return true;
- },
- save: function(db) {
- f = this._file;
- try {
- IO.safeWriteFile(f, db.serialize());
- } catch(e) {
- dump("STS: Error saving db to " + f.path + "!" + e + "\n");
- return false;
- }
- return true;
- }
- };
-